package paypal

import (
	"bytes"
	"context"
	"encoding/base64"
	"errors"
	"fmt"
	"go.manyji.com/lib/config"
	"go.manyji.com/lib/encoding/json"
	"io"
	"net/http"
)

var (
	payPalAPIURL = *config.String("paypal.url", "https://api.sandbox.paypal.com", "地址")
	clientID     = *config.String("paypal.clientid", "AXOSG3EvO4TycaJBbrrjlxME1Actc8gsjqW-MHDiUiKZUsevOg1VB34GVa_Wp5gUgQ_mYmgsfKBXteSD", "密钥")
	clientSecret = *config.String("paypal.secret", "EB1yWRaiWlgmOtF8kzw3J7IjBKcdVO1RZRsH4vnDNPOkXAsVTT_ce4xsgzutNCouz8dYsh6GRYMO1mWT", "密钥")
	returnUrl    = *config.String("paypal.returnurl", "", "成功回调")
	cancelUrl    = *config.String("paypal.cancelurl", "", "失败回调")
)

type PayPalAccessTokenResponse struct {
	AccessToken string `json:"access_token"`
	TokenType   string `json:"token_type"`
}

type PayPalCreateOrderResponse struct {
	ID          string       `json:"id"`
	Links       []PayPalLink `json:"links"`
	PaymentLink string       `json:"paymentLink"`
}

func (or *PayPalCreateOrderResponse) String() string {

	b, _ := json.Marshal(or)
	return string(b)
}

type PayPalLink struct {
	Href   string `json:"href"`
	Rel    string `json:"rel"`
	Method string `json:"method"`
}

func CreateImmediatePaymentOrder(ctx context.Context, amount string) (*PayPalCreateOrderResponse, *ErrorResponse, error) {
	// 获取访问令牌
	accessToken, err := getAccessToken()
	if err != nil {
		return nil, nil, err
	}

	// 创建订单
	order, orderErr, err := createOrder(ctx, accessToken, amount)
	if err != nil {
		return nil, orderErr, err
	}

	return order, orderErr, nil
}

func CaptureOrder(ctx context.Context, orderID string) (*CaptureOrderResponse, *ErrorResponse, error) {
	// 获取访问令牌
	accessToken, err := getAccessToken()
	if err != nil {
		return nil, nil, err
	}

	// 创建订单
	order, orderErr, err := captureOrder(ctx, orderID, accessToken)
	if err != nil {
		return nil, orderErr, err
	}

	return order, nil, nil
}

func GetOrder(ctx context.Context, orderNo string) (*Order, *ErrorResponse, error) {
	// 获取访问令牌
	accessToken, err := getAccessToken()
	if err != nil {
		return nil, nil, err
	}

	// 创建订单
	order, orderErr, err := getOrder(ctx, orderNo, accessToken)
	if err != nil {
		return nil, orderErr, err
	}

	return order, nil, nil
}

func getAccessToken() (string, error) {
	authString := base64Encode(clientID + ":" + clientSecret)
	authHeader := "Basic " + authString

	req, err := http.NewRequest("POST", payPalAPIURL+"/v1/oauth2/token", bytes.NewBuffer([]byte("grant_type=client_credentials")))
	if err != nil {
		return "", err
	}

	req.Header.Set("Authorization", authHeader)
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("认证失败，状态码：%d", resp.StatusCode)
	}

	var tokenResponse PayPalAccessTokenResponse
	err = json.NewDecoder(resp.Body).Decode(&tokenResponse)
	if err != nil {
		return "", err
	}

	return tokenResponse.AccessToken, nil
}

func createOrder(ctx context.Context, accessToken string, amount string) (*PayPalCreateOrderResponse, *ErrorResponse, error) {
	orderData := map[string]interface{}{
		"intent": "CAPTURE",
		"purchase_units": []map[string]interface{}{
			{
				"amount": map[string]interface{}{
					"currency_code": "USD",
					"value":         amount,
				},
			},
		},
		"application_context": map[string]string{
			"return_url": returnUrl,
			"cancel_url": cancelUrl,
		},
	}

	orderJSON, err := json.Marshal(orderData)
	if err != nil {
		return nil, nil, err
	}

	req, err := http.NewRequestWithContext(ctx, "POST", payPalAPIURL+"/v2/checkout/orders", bytes.NewBuffer(orderJSON))
	if err != nil {
		return nil, nil, err
	}

	req.Header.Set("Authorization", "Bearer "+accessToken)
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return nil, nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusCreated {
		return nil, nil, fmt.Errorf("创建订单失败，状态码：%d", resp.StatusCode)
	}

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, nil, err
	}

	var (
		orderResponse    = &PayPalCreateOrderResponse{}
		orderErrResponse = &ErrorResponse{Response: resp}
	)
	err = json.Unmarshal(body, orderResponse)
	if err != nil {
		return nil, nil, err
	}

	if orderResponse.ID == "" {
		err = json.Unmarshal(body, orderErrResponse)
		if err != nil {
			return nil, nil, err
		} else {
			orderErr := errors.New(orderErrResponse.Message)
			if orderErrResponse.Details != nil {
				orderErr = errors.New(orderErrResponse.Details[0].Issue)
			}
			return nil, orderErrResponse, orderErr
		}
	}

	// 订单创建成功，获取订单ID和支付链接
	paymentLink := ""
	for _, link := range orderResponse.Links {
		if link.Rel == "approve" {
			paymentLink = link.Href
			break
		}
	}

	if paymentLink == "" {
		return nil, orderErrResponse, fmt.Errorf("未找到支付链接")
	}

	orderResponse.PaymentLink = paymentLink

	return orderResponse, nil, nil
}

func getOrder(ctx context.Context, orderNo, accessToken string) (*Order, *ErrorResponse, error) {

	req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s%s%s", payPalAPIURL, "/v2/checkout/orders/", orderNo), nil)
	if err != nil {
		return nil, nil, err
	}

	req.Header.Set("Authorization", "Bearer "+accessToken)

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return nil, nil, err
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, nil, err
	}

	var (
		orderResponse    = &Order{}
		orderErrResponse = &ErrorResponse{Response: resp}
	)
	err = json.Unmarshal(body, orderResponse)
	if err != nil {
		return nil, nil, err
	}

	if orderResponse.ID == "" {
		err = json.Unmarshal(body, orderErrResponse)
		if err != nil {
			return nil, nil, err
		} else {
			orderErr := errors.New(orderErrResponse.Message)
			if orderErrResponse.Details != nil {
				orderErr = errors.New(orderErrResponse.Details[0].Issue)
			}
			return nil, orderErrResponse, orderErr
		}
	}

	return orderResponse, nil, nil
}

func captureOrder(ctx context.Context, orderID, accessToken string) (*CaptureOrderResponse, *ErrorResponse, error) {

	req, err := http.NewRequestWithContext(ctx, "POST",
		payPalAPIURL+fmt.Sprintf("/v2/checkout/orders/%v/capture", orderID), nil)
	if err != nil {
		return nil, nil, err
	}

	req.Header.Set("Authorization", "Bearer "+accessToken)
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return nil, nil, err
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, nil, err
	}

	var (
		orderResponse    = &CaptureOrderResponse{}
		orderErrResponse = &ErrorResponse{Response: resp}
	)
	err = json.Unmarshal(body, orderResponse)
	if err != nil {
		return nil, nil, err
	}

	if orderResponse.ID == "" {
		err = json.Unmarshal(body, orderErrResponse)
		if err != nil {
			return nil, nil, err
		} else {
			orderErr := errors.New(orderErrResponse.Message)
			if orderErrResponse.Details != nil {
				orderErr = errors.New(orderErrResponse.Details[0].Issue)
			}
			return nil, orderErrResponse, orderErr
		}
	}

	return orderResponse, nil, nil
}

func createPayment(ctx context.Context, payerId, accessToken string) (string, error) {
	url := payPalAPIURL + "/payments/payment"

	paymentRequest := PaymentRequest{
		Intent: "sale",
		Payer: Payer{
			PaymentMethod: "paypal",
		},
		Transactions: []Transaction{
			{
				Amount: &Amount{
					Total:    "10.00",
					Currency: "USD",
				},
				Description: "Transaction description",
			},
		},
		RedirectURLs: RedirectURLs{
			ReturnURL: "http://example.com/return",
			CancelURL: "http://example.com/cancel",
		},
	}

	paymentJSON, err := json.Marshal(paymentRequest)
	if err != nil {
		return "", err
	}

	req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(paymentJSON))
	if err != nil {
		return "", err
	}

	req.Header.Set("Authorization", "Bearer "+accessToken)
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}

	// Extract the payment ID from the response
	var response map[string]interface{}
	err = json.Unmarshal(body, &response)
	if err != nil {
		return "", err
	}

	paymentID, ok := response["id"].(string)
	if !ok {
		return "", fmt.Errorf("Failed to get payment ID from response")
	}

	return paymentID, nil
}

func executePayment1(paymentLink string) error {

	client := &http.Client{}
	resp, err := client.Get(paymentLink)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	//Optionally, you can read the response body if needed
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	fmt.Println(string(body))

	return nil
}

func executePayment(ctx context.Context, payerId, orderID, accessToken string) error {
	url := payPalAPIURL + "/v2/payments/payment/" + orderID + "/capture"
	//url := payPalAPIURL + "/v2/checkout/orders" + orderID + "/capture"

	executePaymentRequest := ExecutePaymentRequest{
		PayerID: payerId,
	}

	executePaymentJSON, err := json.Marshal(executePaymentRequest)
	if err != nil {
		return err
	}

	req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(executePaymentJSON))
	if err != nil {
		return err
	}

	req.Header.Set("Authorization", "Bearer "+accessToken)
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	// Handle the response as needed
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	fmt.Println("Execute Payment Response:", string(body))

	return nil
}

//func executePayment(ctx context.Context, orderID, accessToken string) error {
//	url := payPalAPIURL + "/v2/checkout/orders/" + orderID + "/capture"
//
//	req, err := http.NewRequestWithContext(ctx, "POST", url, nil)
//	if err != nil {
//		return err
//	}
//
//	req.Header.Set("Authorization", "Bearer "+accessToken)
//	req.Header.Set("Content-Type", "application/json")
//
//	client := &http.Client{}
//	resp, err := client.Do(req)
//	if err != nil {
//		return err
//	}
//	defer resp.Body.Close()
//
//	body, err := io.ReadAll(resp.Body)
//	if err != nil {
//		return err
//	}
//
//	var captureResponse CaptureOrderResponse
//	err = json.Unmarshal(body, &captureResponse)
//	if err != nil {
//		return err
//	}
//
//	if captureResponse.Status != "COMPLETED" {
//		return fmt.Errorf("Order capture failed with status: %s", captureResponse.Status)
//	}
//
//	return nil
//}

func base64Encode(s string) string {
	return base64.StdEncoding.EncodeToString([]byte(s))
}
